package com.jprocms.module.cms.util;

import com.jprocms.module.cms.framework.annotation.CascadeValid;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.COMMON_PARAM_ERROR;

/**
 * @author jprocms
 * 参数联动检验
 */
@Slf4j
public class CascadeValidateUtil {

    public static void check(Object param) {
        if (param == null) {
            return;
        }
        Class cls = param.getClass();
        Field[] fields = cls.getDeclaredFields();
        if (fields != null && fields.length > 0) {
            Map<String, Object> fieldMap = new HashMap<>(10);
            Arrays.stream(fields).forEach(f -> checkField(cls, param, f, fieldMap));
        }

    }

    private static void checkField(Class cls, Object param, Field f, Map<String, Object> fieldMap) {
        CascadeValid relation = f.getAnnotation(CascadeValid.class);
        if (relation == null) {
            return;
        }
        String paramName = relation.param();
        if (StringUtils.isBlank(paramName)) {
            return;
        }
        try {
            Field relativeField = cls.getDeclaredField(paramName);
            relativeField.setAccessible(true);
            Object value = relativeField.get(param);
            CascadeValid.Expression expression = relation.expression();
            if (isMatchCondition(expression, value, relation.value())) {
                f.setAccessible(true);
                Object v = f.get(param);
                boolean isEmpty = isEmpty(v);
                if (!isEmpty) {
                    if (v instanceof String) {
                        isEmpty = StringUtils.isBlank((String) v);
                    } else if (v instanceof Collection) {
                        isEmpty = CollectionUtils.isEmpty((Collection) v);
                    } else if (v instanceof Map) {
                        isEmpty = MapUtils.isEmpty((Map) v);
                    }
                }
                if (isEmpty) {
                    String errorMsg = relation.message();
                    if (StringUtils.isBlank(errorMsg)) {
                        errorMsg = String.format("%s的属性%s不能为空", cls.getSimpleName(), f.getName());
                    }
                    log.error(errorMsg);
                    throw exception(COMMON_PARAM_ERROR);
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
        }
    }

    public static boolean isEmpty(Object v) {
        if (v == null) {
            return true;
        }
        if (v instanceof String) {
            return StringUtils.isBlank((String) v);
        } else if (v instanceof Collection) {
            return CollectionUtils.isEmpty((Collection) v);
        } else if (v instanceof Map) {
            return MapUtils.isEmpty((Map) v);
        } else if (v.getClass().isArray()) {
            return Array.getLength(v) == 0;
        }
        return false;
    }

    private static boolean isMatchCondition(CascadeValid.Expression expression, Object value,
                                            String value1) {
        switch (expression) {
            case NOT_NULL:
                if (value == null) {
                    return false;
                }
                if (value.getClass() == String.class) {
                    return StringUtils.isNotBlank((String) value);
                }
                return true;
            case EQ:
                if (value == null) {
                    return false;
                }
                return Objects.equals(value.toString(), value1);
            case NE:
                if (value == null) {
                    return false;
                }
                return !Objects.equals(value.toString(), value1);
            case IN:
                String[] list = value1.split(",");
                if (list == null || value == null) {
                    return false;
                }
                return Arrays.stream(list).anyMatch(item -> Objects.equals(value.toString(), item));
            case NOT_IN:
                String[] list1 = value1.split(",");
                if (list1 == null || value == null) {
                    return false;
                }
                return Arrays.stream(list1)
                        .allMatch(item -> !Objects.equals(value.toString(), item));
            default:
                return false;
        }
    }
}
